package com.zyd.androidwifi.utils;

import static android.content.Context.WIFI_SERVICE;

import android.Manifest;
import android.app.DownloadManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.MacAddress;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiNetworkSpecifier;
import android.net.wifi.WifiNetworkSuggestion;
import android.os.Build;
import android.os.PatternMatcher;
import android.text.format.Formatter;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;

import java.util.ArrayList;
import java.util.List;

public class WifiUtil {
    private ConnectivityManager connectivityManager;
    private WifiManager mWifiManager;

    private ConnectivityManager.NetworkCallback networkCallback;

    public void changeToWifi(String wifiName, String wifiPwd) {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            changeToWifiAfterQ(wifiName, wifiPwd);
        } else {
            changeToWifiBeforeQ(wifiName, wifiPwd);
        }
    }

    /**
     * 切换到指定wifi Android版本小于10
     *
     * @param wifiName 指定的wifi名字
     * @param wifiPwd  wifi密码，如果已经保存过密码，可以传入null
     * @return
     */
    public void changeToWifiBeforeQ(String wifiName, String wifiPwd) {
        if (mWifiManager == null) {
            Log.i(TAG, " ***** init first ***** ");
            return;
        }

        String mWifiName = "\"" + wifiName + "\"";

        /**
         * 判断定位权限
         */
        if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {

            return;
        }
        //获取wifi列表
        List wifiList = mWifiManager.getConfiguredNetworks();
        boolean bFindInList = false;
        for (int i = 0; i < wifiList.size(); ++i) {
            WifiConfiguration wifiInfo0 = (WifiConfiguration) wifiList.get(i);

            // 先找到对应的wifi
            if (mWifiName.equals(wifiInfo0.SSID) || wifiName.equals(wifiInfo0.SSID)) {
                // 1、 先启动，可能已经输入过密码，可以直接启动
                Log.i(TAG, " set wifi 1 = " + wifiInfo0.SSID);
                doChange2Wifi(wifiInfo0.networkId);
                return;
            }
        }

        // 2、如果wifi还没有输入过密码，尝试输入密码，启动wifi
        if (!bFindInList) {
            WifiConfiguration wifiNewConfiguration = createWifiInfo(wifiName, wifiPwd);//使用wpa2的wifi加密方式
            int newNetworkId = mWifiManager.addNetwork(wifiNewConfiguration);
            if (newNetworkId == -1) {
                Log.e(TAG, "操作失败,需要您到手机wifi列表中取消对设备连接的保存");
            } else {
                doChange2Wifi(newNetworkId);
            }
        }
    }


    /**
     * 切换到指定wifi Android版本大于等于10
     *
     * @param wifiName 指定的wifi名字
     * @param wifiPwd  wifi密码，如果已经保存过密码，可以传入null
     * @return
     */
    @RequiresApi(api = Build.VERSION_CODES.Q)
    public void changeToWifiAfterQ(String wifiName, String wifiPwd) {
        Log.i(TAG, " ***** changeToWifiAfterQ first ***** ");
        if (mWifiManager == null || connectivityManager == null) {
            Log.i(TAG, " ***** init first ***** ");
            return;
        }

        /**
         * 判断定位权限
         */
        if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {

            return;
        }
        //获取wifi扫描列表
//        List<ScanResult> wifiList = mWifiManager.getScanResults();
//        ScanResult scan = null;
//        for (ScanResult scanResult : wifiList) {
//            if (wifiName.equals(scanResult.SSID)) {
//                scan = scanResult;
//                break;
//            }
//        }
        //扫描到了Wi-Fi
//        if (null != scan) {
        //setSsidPattern/setSsid/setBssidPattern/setBssid should be invoked for specifier
        NetworkSpecifier specifier = new WifiNetworkSpecifier.Builder()
//                        .setSsidPattern(new PatternMatcher("test", PatterMatcher.PATTERN_PREFIX))
                .setSsid(wifiName)
                .setWpa2Passphrase(wifiPwd)
//                .setBssid(MacAddress.fromString(scan.BSSID))
                .build();

        NetworkRequest request =
                new NetworkRequest.Builder()
                        .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                        .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                        .setNetworkSpecifier(specifier)
                        .build();

        networkCallback = new ConnectivityManager.NetworkCallback() {
            @Override
            public void onAvailable(@NonNull Network network) {
                super.onAvailable(network);
                Log.i(TAG, "onAvailable 切换到指定wifi成功"+network);
                connectivityManager.bindProcessToNetwork(network);
                connectivityManager.getNetworkInfo(network);
                NetworkInfo networkInfo = connectivityManager.getNetworkInfo(network);
                boolean connected = networkInfo.isConnected();

                boolean available = networkInfo.isAvailable();
                NetworkInfo.DetailedState detailedState = networkInfo.getDetailedState();
                Log.i(TAG, "onAvailable connected= "+connected);
                Log.i(TAG, "onAvailable available= "+available);
                Log.i(TAG, "onAvailable detailedState= "+detailedState);

            }

            @Override
            public void onUnavailable() {
                super.onUnavailable();
                Log.i(TAG, "onUnavailable 切换到指定wifi失败");
                connectivityManager.unregisterNetworkCallback(networkCallback);
            }
        };
//        connectivityManager.registerNetworkCallback(request, networkCallback);
        connectivityManager.requestNetwork(request, networkCallback);

//        }else{
//            Log.e(TAG, "未找到目标Wi-Fi");
//        }

    }

    private boolean doChange2Wifi(int newNetworkId) {
        // 如果wifi权限没打开（1、先打开wifi，2，使用指定的wifi
        if (!mWifiManager.isWifiEnabled()) {
            mWifiManager.setWifiEnabled(true);
        }

        boolean enableNetwork = mWifiManager.enableNetwork(newNetworkId, true);
        if (!enableNetwork) {
            Log.e(TAG, "切换到指定wifi失败");
            return false;
        } else {
            Log.e(TAG, "切换到指定wifi成功");
            return true;
        }
    }

    /**
     * 创建 WifiConfiguration，这里创建的是wpa2加密方式的wifi
     *
     * @param ssid     wifi账号
     * @param password wifi密码
     * @return
     */
    private WifiConfiguration createWifiInfo(String ssid, String password) {
        WifiConfiguration config = new WifiConfiguration();
        config.allowedAuthAlgorithms.clear();
        config.allowedGroupCiphers.clear();
        config.allowedKeyManagement.clear();
        config.allowedPairwiseCiphers.clear();
        config.allowedProtocols.clear();
        config.SSID = "\"" + ssid + "\"";
        config.preSharedKey = "\"" + password + "\"";
        config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
        config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
        config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
        config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
        config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
        config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
        config.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
        config.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
        config.status = WifiConfiguration.Status.ENABLED;
        return config;
    }


//    /**
//     * 创建连接
//     * @param manager WifiManager
//     * @param ssid  Wifi名称
//     * @param bssid 唯一标识（可以为空）
//     * @param passwd    密码  （当前网络是开放网络时，可以为空）
//     * @param isHidden  是否是隐藏网络
//     * @param capabilities  安全协议（根据协议选择连接方式）
//     */
//    @RequiresApi(api = Build.VERSION_CODES.Q)
//    public void connectWifiForQ(String ssid, String bssid, String passwd, String capabilities) {
//        if (capabilities.contains("WPA-PSK") || capabilities.contains("WPA2-PSK")) {
//            setWPA2ForQ(mWifiManager, ssid, bssid, passwd, false);
//        } else {
//            setESSForQ(mWifiManager, ssid, false);
//        }
//    }
//
//    // WPA2-PSK
//    @RequiresApi(api = Build.VERSION_CODES.Q)
//    public int setWPA2ForQ(WifiManager manager, String ssid, String bssid, String passwd, boolean isHidden) {
//        WifiNetworkSuggestion suggestion;
//        if (bssid == null) {
//            suggestion= new WifiNetworkSuggestion.Builder()
//                    .setSsid(ssid)
//                    .setWpa2Passphrase(passwd)
//                    .setIsHiddenSsid(isHidden)
//                    .build();
//        } else {
//            suggestion= new WifiNetworkSuggestion.Builder()
//                    .setSsid(ssid)
//                    .setBssid(MacAddress.fromString(bssid))
//                    .setWpa2Passphrase(passwd)
//                    .setIsHiddenSsid(isHidden)
//                    .build();
//        }
//        List<WifiNetworkSuggestion> suggestions = new ArrayList<>();
//        suggestions.add(suggestion);
//        int status = manager.addNetworkSuggestions(suggestions);
//        if (status != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) {
//            // 连接失败
//            Log.d("WifiUtils", "setWPA2ForQ: 添加失败");
//        } else {
//            Log.d("WifiUtils", "setWPA2ForQ: 添加成功");
//        }
//        return status;
//    }
//
//    // ESS
//    @RequiresApi(api = Build.VERSION_CODES.Q)
//    public int setESSForQ(WifiManager manager, String ssid, boolean isHidden) {
//        WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
//                .setSsid(ssid)
//                .setIsHiddenSsid(isHidden)
//                .build();
//        List<WifiNetworkSuggestion> suggestions = new ArrayList<>();
//        suggestions.add(suggestion);
//        int status = manager.addNetworkSuggestions(suggestions);
//        if (status != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) {
//            // 连接失败
//            Log.d("WifiUtils", "setWPA2ForQ: 添加失败");
//        } else {
//            Log.d("WifiUtils", "setWPA2ForQ: 添加成功");
//        }
//        return status;
//
//    }


    public static final String TAG = "WifiUtil";

    private static final WifiUtil ourInstance = new WifiUtil();
    private static Context mContext;

    public static WifiUtil getIns() {
        return ourInstance;
    }





    public void init(Context context) {
        mContext = context;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            connectivityManager = (ConnectivityManager)
                    mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
        }
        mWifiManager = (WifiManager) mContext.getSystemService(WIFI_SERVICE);
    }


    private final String LOGTAG = "Connecting Wifi AP";
    public boolean OnConnectionInit = true; // is used to prevent multiple api calls with OnAvailable event
    public void ConnectToAP(String Ssid, String wifipassword) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            final WifiManager wifiManager = (WifiManager) mContext.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
            final NetworkSpecifier specifier =
                    new WifiNetworkSpecifier.Builder()
                            .setSsid(Ssid)
                            .setWpa2Passphrase(wifipassword)
                            .build();
            final NetworkRequest request =
                    new NetworkRequest.Builder()
                            .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                            .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                            .setNetworkSpecifier(specifier)
                            .build();


            final ConnectivityManager connectivityManager = (ConnectivityManager)
                    mContext.getSystemService(Context.CONNECTIVITY_SERVICE);

            final String InhomeSSID = wifiManager.getConnectionInfo().getSSID();
            Log.d(LOGTAG, "Current SSID: " + InhomeSSID);


            Log.d(LOGTAG, "build up Wifi connection");
            final ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() {
                @Override
                public void onAvailable(Network network) {
                    // do success processing here..
                    connectivityManager.bindProcessToNetwork(network);
                    Log.d(LOGTAG, "Network available");
                    String wifiName = wifiManager.getConnectionInfo().getSSID();
                    Log.d(LOGTAG, "New SSID: " + wifiName);
                    WifiInfo Info = wifiManager.getConnectionInfo();
                    String IPAddress = Formatter.formatIpAddress(Info.getIpAddress());
                    Log.d(LOGTAG, "New IPAddress: " + IPAddress);

                    if (OnConnectionInit) {
                        // set wifi config request:
                        HttpFetchAndUnregister("http://10.0.0.1/cgi-bin/ui_system.cgi?cmd=get_config", connectivityManager, this);
                        OnConnectionInit = false;
                    }
                }
                @Override
                public void onUnavailable() {
                    // do failure processing here..
                    Log.d(LOGTAG, "Network unavailable");
                    AlertDialog EndDialog = new AlertDialog.Builder(mContext).create();
                    EndDialog.setTitle("Error connecting");
                    EndDialog.setMessage("Could not connect to " + Ssid);
                    EndDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "OK",
                            new DialogInterface.OnClickListener() {
                                public void onClick(DialogInterface dialog, int which) {
                                    dialog.dismiss();
                                }
                            });
                    EndDialog.show();
                }
            };
            connectivityManager.requestNetwork(request, networkCallback);
            Log.d(LOGTAG, "Network requested");
            connectivityManager.registerNetworkCallback(request, networkCallback);
            Log.d(LOGTAG, "Network registered");
        }
    }

    private void HttpFetchAndUnregister(String Url, ConnectivityManager connectivityManager, ConnectivityManager.NetworkCallback networkConnectionReceiver) {
        RequestQueue requestQueue = Volley.newRequestQueue(mContext);
        Log.d(LOGTAG, "url: " + Url);
        StringRequest stringRequest = new StringRequest(Request.Method.GET, Url, new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                //This code is executed if the server responds, whether or not the response contains data.
                Log.d(LOGTAG, "Http request response: " + response);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    connectivityManager.unregisterNetworkCallback(networkConnectionReceiver);
                }
            }
        }, new Response.ErrorListener() { //Create an error listener to handle errors appropriately.
            @Override
            public void onErrorResponse(VolleyError error) {
                //This code is executed if there is an error.
                Log.d(LOGTAG, "error: " + error);
            }
        });
        requestQueue.add(stringRequest);
    }
}
